<?php
/**
 * lsys task
 * @author     Lonely <shan.liu@msn.com>
 * @copyright  (c) 2017 Lonely <shan.liu@msn.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0
 */
namespace LSYS;
use LSYS\Cron\Runer;
use LSYS\Cron\Job;
use LSYS\Cron\ExeJob;
use LSYS\Cron\RunJob;
use LSYS\Cron\Timer;
use \Redis;

class Cron{
	public static $redis_sleep="ltask_sleep";
	public static $redis_job_prefix="ltask_job:";
	public static $redis_job_list="ltask_list";
	/**
	 * @var \Redis
	 */
	protected $redis;
	/**
	 * 任务管理类
	 * @param Redis $redis
	 */
	public function __construct(Redis $redis){
		$this->redis=$redis;
		$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
	}
	/**
	 * 设置一个任务
	 * @param Job $job
	 * @param string $overwrite
	 * @return boolean
	 */
	public function set_job(Job $job,$overwrite=true){
		$next_run_time=$job->get_next_run_time();
		if ($next_run_time===false)$next_run_time=time();
		$timer=serialize($job->get_timer());
		$runer=$job->get_runer_string();
		if (!$overwrite
				&&$this->redis->sismember(static::$redis_job_list,$name)){
					//不重写
					return false;
		}
		$name=static::$redis_job_prefix.$job->get_name();
		$this->redis->multi();
		$this->redis->hset($name,'timer',$timer);
		$this->redis->hset($name,'runer',$runer);
		$this->redis->hset($name,'skip',intval($job->get_skip()));
		$this->redis->hset($name,'run_time',$next_run_time);
		$this->redis->sAdd(static::$redis_job_list,$job->get_name());
		if(!$this->redis->exec()) return false;
		//添加一个任务
		$this->un_sleep();
		return true;
	}
	/**
	 * 把指定任务修改到下一个执行时间
	 * @param Job $job
	 * @param unknown $next_run_time
	 * @return boolean
	 */
	public function next_time_job(Job $job,$next_run_time){
		if ($next_run_time===false){
			$this->del_job($job->get_name());
			return false;
		}
		$name=static::$redis_job_prefix.$job->get_name();
		$this->redis->hset($name,'run_time',$next_run_time);
		return true;
	}
	/**
	 * 执行一个暂停
	 * @param int $time
	 * @return \LSYS\Cron
	 */
	public function on_sleep($time){
		$time=intval($time);
		$time=$time<=0?0:$time;
		$this->redis->brPop(static::$redis_sleep,$time);
		return $this;
	}
	/**
	 * 解除一个暂停
	 * @return \LSYS\Cron
	 */
	public function un_sleep(){
		$this->redis->lPush(static::$redis_sleep,'0');//
		return $this;
	}
	/**
	 * 删除一个任务
	 * @param string $job_name
	 * @return boolean
	 */
	public function del_job($job_name){
		$name=static::$redis_job_prefix.$job_name;
		$this->redis->multi();
		$this->redis->del($name);
		$this->redis->srem(static::$redis_job_list,$job_name);
		if(!$this->redis->exec()) return false;
		$this->un_sleep();
		return true;
	}
	/**
	 * 获取待执行任务列表
	 * @return RunJob[]
	 */
	public function run_job_list(){
		$list=$this->redis->sMembers(static::$redis_job_list);
		$out=array();
		foreach ($list as $v){
			$name=static::$redis_job_prefix.$v;
			$runer=$this->redis->hGet($name,'runer');
			$timer=unserialize($this->redis->hGet($name,'timer'));
			$run_time=intval($this->redis->hGet($name,'run_time'));
			if ((!$timer instanceof Timer)||$run_time<=0)continue;
			$job=new Job($v, $runer);
			$job->set_timer($timer);
			$job->set_skip($this->redis->hGet($name,'skip'));
			$out[]=new RunJob($job, $run_time);
		}
		return $out;
	}
	/**
	 * 派发指定任务
	 * @param Job $job
	 */
	public function exec_job(Job $job){
		ExeJob::set_redis($this->redis);
		return ExeJob::instance()->exec($job);
	}
	/**
	 * 获取任务执行结果列表
	 * 默认只保留最后100条结果,可修改 ExeJob::$redis_job_log_limit 
	 * @param number $offset
	 * @param number $limit
	 * @return array[
	 * 	'0'=>执行时间
	 * 	'1'=>执行对象
	 * 	'2'=>执行输出
	 * ]
	 */
	public function job_result($offset=0,$limit=10){
		//获取最后几个运行结果
		$data= $this->redis->lrange(ExeJob::$redis_job_log,$offset,$limit);
		$out=array();
		foreach($data as $v){
			$out[]=json_decode($v,true);
		}
		unset($data);
		return $out;
	}
}